package com.lzc.springPart.BeanFactoryPostProcess;

import com.sun.org.apache.xalan.internal.xsltc.compiler.Pattern;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ComponentScanPostProcess implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        // 获取所有bean定义的名称
        String[] beanDefinitionNames = configurableListableBeanFactory.getBeanDefinitionNames();
        // 遍历所有bean定义的名称
        for (String definitionName : beanDefinitionNames){
            // 根据bena定义名称 获取bean定义
            BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(definitionName);
            try {
                // 从bean定义中获取该bean的class类路径名称，从而得到类对象
                Class<?> aClass = Class.forName(beanDefinition.getBeanClassName());
                // 获取该class对象中所有的@ComponentScan
                ComponentScan[] annotationsByType = aClass.getAnnotationsByType(ComponentScan.class);
                // 遍历得到里面的value值
                for (ComponentScan componentScan : annotationsByType){
                    String[] values = componentScan.value();
                    for (String scanPackage : values){
                        // 根据value扫描对应的包
                        scanAddBeanDefinition(scanPackage, configurableListableBeanFactory);
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 根据ComponentScan中的包路径加载有Component注解的对应的beanDefinition
    private void scanAddBeanDefinition(String scanPackage, ConfigurableListableBeanFactory configurableListableBeanFactory){
        List<Class> packageAllClass = getPackageAllClass(scanPackage);
        // 判断是否有@Component
        for (Class aClass : packageAllClass) {
            Annotation componentAnnotation = aClass.getAnnotation(Component.class);
            Annotation configurationAnnotation = aClass.getAnnotation(Configuration.class);
            if (componentAnnotation == null && configurationAnnotation == null){
                continue;
            }
            // 有Component或者configuration则加入beanDefinition
            if (configurableListableBeanFactory instanceof DefaultListableBeanFactory){
                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(aClass).setScope("singleton").getBeanDefinition();
                beanFactory.registerBeanDefinition(aClass.getSimpleName().toLowerCase(),beanDefinition);
            }
        }

    }

    // 根据ComponentScan中的包路径获取所有的class对象
    private List<Class> getPackageAllClass(String scanPackage){
        // 保存该包下的所有class对象
        ArrayList<Class> classes = new ArrayList<>();
        // 获取绝对路径
        ClassPathResource classPathResource = new ClassPathResource(scanPackage);
        String string = scanPackage.replace(".", "/");
        String path = classPathResource.getClassLoader().getResource(string).getPath();
        // 得到该绝对路径的File对象
        File file = new File(path);
        // 得到该目录中的所有类对象
        getAllClass(file, classes, scanPackage);

        return classes;
    }

    /**
     * 根据File对象得到所有的class类对象
     * @param file File对象
     * @param classes class对象集合
     * @param packagePath class所在的包
     * @return
     */
    private void getAllClass(File file, ArrayList<Class> classes, String packagePath){
        if (!file.exists()){
            return;
        }
        // 获取到该目录下的所有文件
        for (File childerfile : file.listFiles()) {
            // 如果是文件
            if (childerfile.isFile()){
                // 得到文件名
                String className = childerfile.getName();
                // 判断是否为class文件
                if (!className.matches("(.*).class")){
                    continue;
                }
                // 得到全路径类名
                className = packagePath+"."+className;
                className = className.split(".class")[0];
                // 获取class对象
                try {
                    classes.add(Class.forName(className));
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("getPackageAllClass Class.forName("+className+") error");
                }
            }else {
                getAllClass(childerfile, classes, packagePath+"."+childerfile.getName());
            }

        }
    }

}